#include "analyzer_common.h"

int analyze_symmetric(sparse_matrix_t* s){

}

int analyze_rowlen(sparse_matrix_t* s){
    sparse_index_t bucket_len = 10;
    sparse_index_t* bucket = malloc(sizeof(sparse_index_t) * bucket_len);
    memset(bucket, 0, sizeof(sparse_index_t) * bucket_len);

    sparse_index_t min_len = s->m;
    sparse_index_t max_len = 0;
    for(sparse_index_t row = 0; row < s->m; row++){
        sparse_index_t len = s->rowpointer[row+1] - s->rowpointer[row];
        if(len < min_len){
            min_len = len;
        }
        if(len > max_len){
            max_len = len;
        }
    }
    sparse_index_t len_stride = (max_len - min_len + bucket_len - 1) / bucket_len;
    for(sparse_index_t row = 0; row < s->m; row++){
        sparse_index_t len = s->rowpointer[row+1] - s->rowpointer[row];
        bucket[(len - min_len) / len_stride]++;
    }

    printf("min_len = %d max_len = %d\n", min_len, max_len);
    for(int bucket_id = 0; bucket_id < bucket_len; bucket_id++){
        if(bucket[bucket_id]==0){
            continue;
        }
        printf("%10d ~ %10d :%10d (%5.2lf%%)\n", bucket_id * len_stride, MIN((bucket_id+1) * len_stride, s->m), bucket[bucket_id], ((double)bucket[bucket_id]) / s->m);
    }
}

// int analyze_rowlen(sparse_matrix_t* s){
//     sparse_index_t bucket_len = 342;
//     sparse_index_t* bucket = malloc(sizeof(sparse_index_t) * bucket_len);
//     memset(bucket, 0, sizeof(sparse_index_t) * bucket_len);

//     // sparse_index_t min_len = s->m;
//     // sparse_index_t max_len = 0;
//     // for(sparse_index_t row = 0; row < s->m; row++){
//     //     sparse_index_t len = s->rowpointer[row+1] - s->rowpointer[row];
//     //     if(len < min_len){
//     //         min_len = len;
//     //     }
//     //     if(len > max_len){
//     //         max_len = len;
//     //     }
//     // }
//     // sparse_index_t len_stride = (max_len - min_len + bucket_len - 1) / bucket_len;
//     for(sparse_index_t row = 0; row < s->m; row++){
//         sparse_index_t len = s->rowpointer[row+1] - s->rowpointer[row];
//         // bucket[(len - min_len) / len_stride]++;
//         bucket[len]++;
//         if(len == 341){
//             printf("len==341, row = %d\n" ,row);
//         }
//     }

//     // printf("min_len = %d max_len = %d\n", min_len, max_len);
//     for(int bucket_id = 0; bucket_id < bucket_len; bucket_id++){
//         if(bucket[bucket_id]==0){
//             continue;
//         }
//         printf("%d %d\n", bucket_id, bucket[bucket_id]);
//         // printf("%10d ~ %10d :%10d (%5.2lf%%)\n", bucket_id * len_stride + min_len, MIN((bucket_id+1) * len_stride + min_len, s->m), bucket[bucket_id], ((double)bucket[bucket_id]) / s->m);
//     }
// }


int analyze_valueprec(sparse_matrix_t* s){
    int tolerance_count = 14;
    double tolerances[] = {1e-6, 1e-8, 1e-10, 1e-12, 1e-14, 1e-16, 1e-18, 1e-20, 1e-22, 1e-25, 1e-30, 1e-50, 1e-70, 1e-100};

    sparse_pointer_t* fp32_cnt = malloc(sizeof(sparse_pointer_t) * tolerance_count);
    sparse_pointer_t* fp64_cnt = malloc(sizeof(sparse_pointer_t) * tolerance_count);
    memset(fp32_cnt, 0, sizeof(sparse_pointer_t) * tolerance_count);
    memset(fp64_cnt, 0, sizeof(sparse_pointer_t) * tolerance_count);

    for(int tol_idx = 0; tol_idx < tolerance_count; tol_idx++){
        for(sparse_pointer_t idx = 0; idx < s->nnz; idx++){
            if(fabs(s->value[idx] - (float)s->value[idx]) < tolerances[tol_idx]){
                fp32_cnt[tol_idx]++;
            }else{
                fp64_cnt[tol_idx]++;
            }
        }
        printf("tolerence = %6.0le fp64 : %12lld(%5.2lf%%) fp32 : %12lld(%5.2lf%%)\n", tolerances[tol_idx], 
            fp64_cnt[tol_idx], (100.0 * fp64_cnt[tol_idx])/(fp64_cnt[tol_idx] + fp32_cnt[tol_idx]),
            fp32_cnt[tol_idx], (100.0 * fp32_cnt[tol_idx])/(fp64_cnt[tol_idx] + fp32_cnt[tol_idx])
        );
    }
}

int analyze_smallvalue(sparse_matrix_t* s){
    double tolerence = 1e-100;
    sparse_pointer_t nzero = 0;
    for(sparse_pointer_t idx = 0; idx < s->nnz; idx++){
        if(fabs(s->value[idx]) < tolerence){
            nzero++;
        }
    }
    printf("tolerence = %6.0le zeros : %12lld(%5.2lf%%) non-zero : %12lld(%5.2lf%%)\n", tolerence, 
        nzero, (100.0 * nzero)/s->nnz,
        s->nnz - nzero, (100.0 * (s->nnz - nzero))/s->nnz
    );
}

int analyze_purezerovalue(sparse_matrix_t* s){
    sparse_pointer_t nzero = 0;
    for(sparse_pointer_t idx = 0; idx < s->nnz; idx++){
        if(s->value[idx] == 0.0){
            nzero++;
        }
    }
    printf("zeros : %12lld(%5.2lf%%) non-zero : %12lld(%5.2lf%%)\n", 
        nzero, (100.0 * nzero)/s->nnz,
        s->nnz - nzero, (100.0 * (s->nnz - nzero))/s->nnz
    );
}

int analyze_rowcount_onlyone_nnz(sparse_matrix_t* s){
    double tol = 1e-40;
    sparse_pointer_t nrow_absolutely = 0;
    sparse_pointer_t nrow_smalltol = 0;
    for(sparse_index_t row = 0; row < s->m; row++){
        sparse_index_t nnz_row_smalltol = 0;
        sparse_index_t nnz_row_absolutely = 0;
        for(sparse_pointer_t idx = s->rowpointer[row]; idx < s->rowpointer[row+1]; idx++){
            if(s->value[idx] != 0.0){
                nnz_row_absolutely++;
            }
            if(fabs(s->value[idx]) > tol){
                nnz_row_smalltol++;
            }
        }
        if(nnz_row_smalltol == 1){
            nrow_smalltol++;
        }
        if(nnz_row_absolutely == 1){
            nrow_absolutely++;
        }
        // else{
        //     printf("row %d have %d nnzs.\n", row, nnz_row_absolutely);
        //     for(sparse_pointer_t idx = s->rowpointer[row]; idx < s->rowpointer[row+1]; idx++){
        //         printf("%le\n", s->value[idx]);
        //     }
        // }
    }
    
    printf("tol = %.0le only1_nnz_pure0 = %llu only1_nnz_small = %llu\n", 
        tol, nrow_absolutely, nrow_smalltol
    );
}


int analyze_block_struct(sparse_matrix_t* s){
    const int block_size[] = {2,3,5,7,11,13,17,19};
    const int block_size_len = 8;
    for(int iblk = 0; iblk < block_size_len; iblk++){
        analyze_block_struct_worker(s, block_size[iblk]);
    }
}


int global_pass_cnt = 0;
int run_pass(int (*analyze_pass)(sparse_matrix_t* s), sparse_matrix_t* s, const char* pass_tag){
    global_pass_cnt++;
    sprintf(buf, "Pass #%d %s start.", global_pass_cnt, pass_tag);
    analyzer_log(buf);
    analyze_pass(s);
    sprintf(buf, "Pass #%d %s end.", global_pass_cnt, pass_tag);
    analyzer_log(buf);
}

int matrix_analyze(sparse_matrix_t* s)
{
    // run_pass(analyze_symmetric, s, "analyze_symmetric");
    run_pass(analyze_rowlen, s, "analyze_rowlen");
    run_pass(analyze_rowcount_onlyone_nnz, s, "analyze_rowcount_onlyone_nnz");
    run_pass(analyze_purezerovalue, s, "analyze_zerovalue");
    run_pass(analyze_smallvalue, s, "analyze_zerovalue");
    run_pass(analyze_valueprec, s, "analyze_valueprec");
    run_pass(analyze_block_struct, s, "analyze_block_struct");
}